package service;

import utils.DatabaseUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

public class ScheduleService {
    private static final int WEEKS_PER_SEMESTER = 18;  // 每学期18周
    private static final int SLOTS_PER_DAY = 4;       // 每天4个大节
    private static final int DAYS_PER_WEEK = 5;       // 每周5天

    /**
     * 获取班级课程表
     * @param classId 班级ID
     * @return 该班级的课程表列表，每个元素为一个Map，包含weekday、period、subjectName、teacherName、classroom等信息
     */
    public List<Map<String, Object>> getClassSchedule(Integer classId) {
        if (classId == null || classId <= 0) {
            return new ArrayList<>();
        }

        List<Map<String, Object>> schedules = new ArrayList<>();
        String sql = "SELECT cs.*, st.subject_id, st.teacher_id, " +
                "s.subject_name, u.name as teacher_name, " +
                "CONCAT(cr.building, cr.room_number, '(', cr.capacity, '人)') as classroom " +
                "FROM course_schedules cs " +
                "JOIN subject_teachers st ON cs.subject_teacher_id = st.id " +
                "JOIN subjects s ON st.subject_id = s.id " +
                "JOIN users u ON st.teacher_id = u.id " +
                "JOIN classrooms cr ON cs.classroom_id = cr.id " +
                "WHERE st.class_id = ? " +
                "ORDER BY cs.weekday, cs.period";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setInt(1, classId);
            ResultSet rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> schedule = new HashMap<>();
                schedule.put("weekday", rs.getInt("weekday"));  // 星期几
                schedule.put("period", rs.getInt("period"));    // 大节
                schedule.put("subjectName", rs.getString("subject_name"));  // 课程名称
                schedule.put("teacherName", rs.getString("teacher_name"));  // 教师姓名
                schedule.put("classroom", rs.getString("classroom"));  // 教室信息
                schedules.add(schedule);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return schedules;
    }

    /**
     * 获取教师课程表
     * @param teacherId 教师ID
     * @return 该教师的课程表列表，每个元素为一个Map，包含weekday、period、subjectName、className、teacherName、classroom等信息
     */
    public List<Map<String, Object>> getTeacherSchedule(int teacherId) {
        List<Map<String, Object>> schedules = new ArrayList<>();
        String sql = "SELECT cs.weekday, cs.period, s.subject_name, " +
                "CONCAT(cr.building, ' ', cr.room_number, ' ', cr.type) as classroom, " +
                "cl.class_name, " +
                "u.name as teacher_name " +  // 添加教师姓名
                "FROM subject_teachers st " +
                "JOIN course_schedules cs ON cs.subject_teacher_id = st.id " +
                "JOIN subjects s ON st.subject_id = s.id " +
                "JOIN classrooms cr ON cs.classroom_id = cr.id " +
                "JOIN classes cl ON st.class_id = cl.id " +
                "JOIN users u ON st.teacher_id = u.id " +  // 关联用户表获取教师姓名
                "WHERE st.teacher_id = ? " +
                "ORDER BY cs.weekday, cs.period";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {

            stmt.setInt(1, teacherId);
            ResultSet rs = stmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> schedule = new HashMap<>();
                schedule.put("weekday", rs.getInt("weekday"));  // 星期几
                schedule.put("period", rs.getInt("period"));    // 大节
                schedule.put("subjectName", rs.getString("subject_name"));  // 课程名称
                schedule.put("className", rs.getString("class_name"));  // 班级名称
                schedule.put("teacherName", rs.getString("teacher_name"));  // 教师姓名
                schedule.put("classroom", rs.getString("classroom"));  // 教室信息
                schedules.add(schedule);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return schedules;
    }

    /**
     * 自动排课
     * @param classId 班级ID
     * @return 排课是否成功
     */
    public boolean autoSchedule(int classId) {
        // 1. 获取班级所有课程和任课教师
        List<Map<String, Object>> subjects = getClassSubjectsToSchedule(classId);
        if (subjects.isEmpty()) return false;

        // 2. 计算每周所需课时
        Map<Integer, Integer> weeklyHours = calculateWeeklyHours(subjects);

        // 3. 开始排课
        Connection conn = null;
        try {
            conn = DatabaseUtil.getConnection();
            conn.setAutoCommit(false);

            for (Map<String, Object> subject : subjects) {
                int subjectTeacherId = (int) subject.get("subject_teacher_id");
                int totalHours = (int) subject.get("hours");  // 总学时

                // 计算每周需要的大节数（每大节2小节）
                int weeklySlots = weeklyHours.get(subjectTeacherId);
                if (weeklySlots <= 0) continue;

                // 为每周安排固定的课时
                if (!scheduleWeeklySlots(subjectTeacherId, weeklySlots)) {
                    conn.rollback();
                    return false;
                }
            }

            conn.commit();
            return true;
        } catch (SQLException e) {
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 计算每周所需课时
     * @param subjects 需要排课的课程列表
     * @return 每个任课教师每周所需的课时数
     */
    private Map<Integer, Integer> calculateWeeklyHours(List<Map<String, Object>> subjects) {
        Map<Integer, Integer> weeklyHours = new HashMap<>();

        for (Map<String, Object> subject : subjects) {
            int subjectTeacherId = (int) subject.get("subject_teacher_id");
            int totalHours = (int) subject.get("hours");  // 总学时

            // 计算每周需要的大节数（每大节2小节）
            int weeklySlots = (int) Math.ceil((double) totalHours / WEEKS_PER_SEMESTER / 2);
            weeklyHours.put(subjectTeacherId, weeklySlots);
        }

        return weeklyHours;
    }

    /**
     * 为课程安排每周的固定课时
     * @param subjectTeacherId 任课教师ID
     * @param weeklySlots 每周所需的大节数
     * @return 安排是否成功
     */
    private boolean scheduleWeeklySlots(int subjectTeacherId, int weeklySlots) {
        // 获取所有可用的时间段
        List<TimeSlot> availableSlots = generateTimeSlots();
        List<TimeSlot> selectedSlots = new ArrayList<>();

        // 尝试找到合适的时间段
        for (int i = 0; i < weeklySlots; i++) {
            TimeSlot bestSlot = findBestTimeSlot(availableSlots, selectedSlots, subjectTeacherId);
            if (bestSlot == null) {
                return false;  // 无法找到合适的时间段
            }
            selectedSlots.add(bestSlot);
            availableSlots.remove(bestSlot);
        }

        // 安排到数据库
        for (TimeSlot slot : selectedSlots) {
            List<Map<String, Object>> availableClassrooms =
                    getAvailableClassrooms(slot.weekday, slot.period);

            if (availableClassrooms.isEmpty()) {
                return false;
            }

            // 选择第一个可用教室
            Map<String, Object> classroom = availableClassrooms.get(0);
            if (!scheduleClass(subjectTeacherId, slot.weekday, slot.period,
                    (Integer)classroom.get("id"))) {
                return false;
            }
        }

        return true;
    }

    /**
     * 找到最佳的时间段
     * @param availableSlots 所有可用的时间段
     * @param selectedSlots 已选的时间段
     * @param subjectTeacherId 任课教师ID
     * @return 最佳的时间段，如果没有找到则返回null
     */
    private TimeSlot findBestTimeSlot(List<TimeSlot> availableSlots,
                                      List<TimeSlot> selectedSlots, int subjectTeacherId) {
        // 优先选择不相邻的日期，使课程分布更均匀
        for (TimeSlot slot : availableSlots) {
            boolean isGoodSlot = true;

            // 检查是否与已选时间段在同一天
            for (TimeSlot selected : selectedSlots) {
                if (selected.weekday == slot.weekday ||
                        Math.abs(selected.weekday - slot.weekday) == 1) {
                    isGoodSlot = false;
                    break;
                }
            }

            if (isGoodSlot) {
                List<Map<String, Object>> classrooms = getAvailableClassrooms(slot.weekday, slot.period);
                for (Map<String, Object> classroom : classrooms) {
                    if (isSlotAvailable(slot, classroom, subjectTeacherId)) {
                        return slot;
                    }
                }
            }
        }

        // 如果找不到理想的时间段，则选择任意可用时间段
        for (TimeSlot slot : availableSlots) {
            List<Map<String, Object>> classrooms = getAvailableClassrooms(slot.weekday, slot.period);
            for (Map<String, Object> classroom : classrooms) {
                if (isSlotAvailable(slot, classroom, subjectTeacherId)) {
                    return slot;
                }
            }
        }

        return null;
    }

    /**
     * 检查时间段是否可用
     * @param slot 时间段
     * @param classroom 教室
     * @param subjectTeacherId 任课教师ID
     * @return 时间段是否可用
     */
    private boolean isSlotAvailable(TimeSlot slot, Map<String, Object> classroom, int subjectTeacherId) {
        String sql = "SELECT 1 FROM course_schedules cs " +
                "JOIN subject_teachers st ON cs.subject_teacher_id = st.id " +
                "WHERE (cs.weekday = ? AND cs.period = ? AND cs.classroom_id = ?) " +
                "OR (cs.weekday = ? AND cs.period = ? AND st.teacher_id = " +
                "(SELECT teacher_id FROM subject_teachers WHERE id = ?))";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setInt(1, slot.weekday);  // 设置星期几
            pstmt.setInt(2, slot.period);    // 设置大节
            pstmt.setInt(3, (Integer)classroom.get("id"));  // 设置教室ID
            pstmt.setInt(4, slot.weekday);  // 设置星期几
            pstmt.setInt(5, slot.period);    // 设置大节
            pstmt.setInt(6, subjectTeacherId);  // 设置任课教师ID

            ResultSet rs = pstmt.executeQuery();
            return !rs.next();  // 如果没有记录，则时间段可用
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    private static class TimeSlot {
        int weekday;  // 星期几
        int period;   // 大节

        TimeSlot(int weekday, int period) {
            this.weekday = weekday;
            this.period = period;
        }
    }

    /**
     * 获取班级待排课（修改SQL以包含学时信息）
     * @param classId 班级ID
     * @return 班级中未排课的课程列表，每个元素为一个Map，包含subject_teacher_id、subject_name、hours、teacher_name等信息
     */
    private List<Map<String, Object>> getClassSubjectsToSchedule(int classId) {
        List<Map<String, Object>> subjects = new ArrayList<>();
        String sql = "SELECT st.id as subject_teacher_id, s.subject_name, " +
                "s.hours, u.name as teacher_name " +
                "FROM subject_teachers st " +
                "JOIN subjects s ON st.subject_id = s.id " +
                "JOIN users u ON st.teacher_id = u.id " +
                "WHERE st.class_id = ? " +
                "AND st.id NOT IN (SELECT subject_teacher_id FROM course_schedules)";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setInt(1, classId);
            ResultSet rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> subject = new HashMap<>();
                subject.put("subject_teacher_id", rs.getInt("subject_teacher_id"));  // 任课教师ID
                subject.put("subject_name", rs.getString("subject_name"));  // 课程名称
                subject.put("hours", rs.getInt("hours"));  // 学时信息
                subject.put("teacher_name", rs.getString("teacher_name"));  // 教师姓名
                subjects.add(subject);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return subjects;
    }

    /**
     * 生成可用时间段
     * @return 可用时间段列表
     */
    private List<TimeSlot> generateTimeSlots() {
        List<TimeSlot> slots = new ArrayList<>();
        // 周一到周五
        for (int weekday = 1; weekday <= 5; weekday++) {
            // 每天5节课
            for (int period = 1; period <= 5; period++) {
                slots.add(new TimeSlot(weekday, period));
            }
        }
        return slots;
    }

    /**
     * 获取可用教室
     * @param weekday 星期几
     * @param period 大节
     * @return 当前时间段可用的教室列表
     */
    public List<Map<String, Object>> getAvailableClassrooms(int weekday, int period) {
        ClassroomService classroomService = new ClassroomService();
        return classroomService.getAvailableClassrooms(weekday, period);
    }

    /**
     * 获取未排课的课程
     * @param classId 班级ID，如果为null则获取所有未排课的课程
     * @return 未排课的课程列表，每个元素为一个Map，包含id、className、subjectName、teacherName等信息
     */
    public List<Map<String, Object>> getUnscheduledSubjects(Integer classId) {
        List<Map<String, Object>> subjects = new ArrayList<>();
        String sql = "SELECT st.id, s.subject_name, u.name as teacher_name, c.class_name " +
                "FROM subject_teachers st " +
                "JOIN subjects s ON st.subject_id = s.id " +
                "JOIN users u ON st.teacher_id = u.id " +
                "JOIN classes c ON st.class_id = c.id " +
                "WHERE st.id NOT IN (SELECT subject_teacher_id FROM course_schedules) " +
                (classId != null ? "AND st.class_id = ? " : "") +
                "ORDER BY c.class_name, s.subject_name";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {

            if (classId != null) {
                stmt.setInt(1, classId);
            }

            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                Map<String, Object> subject = new HashMap<>();
                subject.put("id", rs.getInt("id"));  // 任课教师ID
                subject.put("className", rs.getString("class_name"));  // 班级名称
                subject.put("subjectName", rs.getString("subject_name"));  // 课程名称
                subject.put("teacherName", rs.getString("teacher_name"));  // 教师姓名
                subjects.add(subject);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return subjects;
    }

    /**
     * 添加新的排课方法
     * @param subjectTeacherId 任课教师ID
     * @param weekday 星期几
     * @param period 大节
     * @param classroomId 教室ID
     * @return 是否成功插入新的排课记录
     */
    public boolean scheduleClass(int subjectTeacherId, int weekday, int period, int classroomId) {
        String sql = "INSERT INTO course_schedules (subject_teacher_id, weekday, period, classroom_id) " +
                "VALUES (?, ?, ?, ?)";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setInt(1, subjectTeacherId);
            pstmt.setInt(2, weekday);
            pstmt.setInt(3, period);
            pstmt.setInt(4, classroomId);

            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取学生课表
     * @param userId 学生用户ID
     * @return 学生的课程表列表，每个元素为一个Map，包含weekday、period、subjectName、teacherName、classroom等信息
     */
    public List<Map<String, Object>> getStudentSchedule(int userId) {
        List<Map<String, Object>> schedules = new ArrayList<>();
        String sql = "SELECT cs.weekday, cs.period, s.subject_name, " +
                "CONCAT(c.building, ' ', c.room_number, ' ', c.type) as classroom, " +
                "u.name as teacher_name " +
                "FROM students stu " +
                "JOIN classes cl ON stu.class_id = cl.id " +
                "JOIN subject_teachers st ON st.class_id = cl.id " +
                "JOIN subjects s ON st.subject_id = s.id " +
                "JOIN users u ON st.teacher_id = u.id " +
                "JOIN course_schedules cs ON cs.subject_teacher_id = st.id " +
                "JOIN classrooms c ON cs.classroom_id = c.id " +
                "WHERE stu.user_id = ?";

        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {

            stmt.setInt(1, userId);
            ResultSet rs = stmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> schedule = new HashMap<>();
                schedule.put("weekday", rs.getInt("weekday"));  // 星期几
                schedule.put("period", rs.getInt("period"));    // 大节
                schedule.put("subjectName", rs.getString("subject_name"));  // 课程名称
                schedule.put("teacherName", rs.getString("teacher_name"));  // 教师姓名
                schedule.put("classroom", rs.getString("classroom"));  // 教室信息
                schedules.add(schedule);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return schedules;
    }
}
